﻿/**
 * @file XPDF_wrapper.cpp
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-12-16
 * @version : 1.0.0.0
 * @brief   : 使用 PDFium 库，实现 PDF 简单操作的几个工具类。
 */

#include "xtypes.h"
#include "XPDF_wrapper.h"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>

////////////////////////////////////////////////////////////////////////////////

#ifndef XASSERT
#include <assert.h>
#define XASSERT(xptr)    assert((xptr))
#endif // XASSERT

////////////////////////////////////////////////////////////////////////////////

#if (defined(_WIN32) || defined(_WIN64))
#include <Windows.h>
#elif defined(__linux__) // linux
#include <stddef.h>
#include <dlfcn.h>
#else // unknow
#error "The current platform is not supported"
#endif // (defined(_WIN32) || defined(_WIN64))

/**********************************************************/
/**
 * @brief 获取当前操作错误码。
 */
x_int32_t last_error(void)
{
#if (defined(_WIN32) || defined(_WIN64))
    return (x_int32_t)GetLastError();
#elif defined(__linux__) // linux
    return errno;
#else // unknow
#error "The current platform is not supported"
    return X_ERR_UNKNOW;
#endif // (defined(_WIN32) || defined(_WIN64))
}

/**********************************************************/
/**
 * @brief 加载动态库。
 */
x_handle_t lib_load(x_cstring_t xszt_path)
{
    x_handle_t xht_lib = X_NULL;

#if (defined(_WIN32) || defined(_WIN64))
    xht_lib = (x_handle_t)LoadLibraryA(xszt_path);
#elif defined(__linux__) // linux
    xht_lib = (x_handle_t)dlopen(xszt_path, RTLD_LAZY );
#else // unknow
#error "The current platform is not supported"
#endif // (defined(_WIN32) || defined(_WIN64))

    return xht_lib;
}

/**********************************************************/
/**
 * @brief 卸载动态库。
 */
x_void_t lib_unload(x_handle_t xht_lib)
{
#if (defined(_WIN32) || defined(_WIN64))
    FreeLibrary((HMODULE)xht_lib);
#elif defined(__linux__) // linux
    dlclose(xht_lib);
#else // unknow
#error "The current platform is not supported"
#endif // (defined(_WIN32) || defined(_WIN64))
}

/**********************************************************/
/**
 * @brief 获取动态库中的函数接口地址。
 */
x_pvoid_t lib_func(x_handle_t xht_lib, x_cstring_t xszt_func)
{
    x_pvoid_t xht_func = X_NULL;

#if (defined(_WIN32) || defined(_WIN64))
    xht_func = (x_pvoid_t)GetProcAddress((HMODULE)xht_lib, xszt_func);
#elif defined(__linux__) // linux
    xht_func = dlsym(xht_lib, xszt_func);
#else // unknow
#error "The current platform is not supported"
#endif // (defined(_WIN32) || defined(_WIN64))

    return xht_func;
}

////////////////////////////////////////////////////////////////////////////////

/**
 * @struct XPDF_FILEACCESS
 * @brief  Structure for custom file access.
 */
typedef struct XPDF_FILEACCESS
{
    /**
     * File length, in bytes.
     */
    x_ulong_t xut_flen;

    /**
     * A function pointer for getting a block of data from specific position.
     * Position is specified by byte offset from beginning of the file.
     * The position and size will never go out range of file length.
     * It may be possible for FPDFSDK to call this function multiple times for same position.
     * Return value: should be non-zero if successful, zero for error.
     */
    x_int32_t (* xfunc_read_block)(
        x_pvoid_t xpvt_param,
        x_ulong_t xut_position,
        x_uchar_t * xct_buffer,
        x_ulong_t xut_size);

    /**
     * A custom pointer for all implementation specific data.
     * This pointer will be used as the first parameter to xfunc_read_block callback.
     */
    x_handle_t xht_param;
} XPDF_FILEACCESS;

/**
 * @struct XPDF_FILEWRITE
 * @brief  Structure for custom file write.
 */
typedef struct XPDF_FILEWRITE
{
    /**
     * Version number of the interface. Currently must be 1.
     */
    x_int32_t xit_version;

    /**
     * Method: xfunc_write_block
     *          Output a block of data in your custom way.
     * Interface Version:
     *          1
     * Implementation Required:
     *          Yes
     * Comments:
     *          Called by function FPDF_SaveDocument
     * Parameters:
     *          xfw_this_ptr   -   Pointer to the structure itself
     *          xpvt_dptr      -   Pointer to a buffer to output
     *          xut_size       -   The size of the buffer.
     * Return value:
     *          Should be non-zero if successful, zero for error.
     */
    x_int32_t (* xfunc_write_block)(
        struct XPDF_FILEWRITE * xfw_this_ptr,
        const x_pvoid_t xpvt_dptr,
        x_ulong_t xut_size);

    /**
     * A custom pointer for all implementation specific data.
     */
    x_handle_t xht_param;
} XPDF_FILEWRITE;

/**
 * @struct XFX_FILEAVAIL
 * @brief  Interface for checking whether the section of the file is available. 
 */
typedef struct XFX_FILEAVAIL
{
    /**
     * Version number of the interface. Currently must be 1.
     */
    x_int32_t xit_version;

    /**
     * Method: xfunc_is_data_avail
     *        Report whether the specified data section is available.
     *       A section is available only if all bytes in the section is available. 
     * Interface Version:
     *        1
     * Implementation Required:
     *        Yes
     * Parameters:
     *        xthis_ptr        -    Pointer to the interface structure itself.
     *        xst_offset        -    The offset of the data section in the file.
     *        xst_size        -    The size of the data section
     * Return Value:
     *        true means the specified data section is available.
     * Comments:
     *        Called by Foxit SDK to check whether the data section is ready.
     */
    x_uchar_t (* xfunc_is_data_avail)(
        struct XFX_FILEAVAIL * xthis_ptr,
        x_size_t xst_offset,
        x_size_t xst_size);
} XFX_FILEAVAIL;

/**
 * @struct XFX_DOWNLOADHINTS
 * @brief  Download hints interface. Used to receive hints for further downloading.
 */
typedef struct XFX_DOWNLOADHINTS
{
    /**
     * Version number of the interface. Currently must be 1.
     */
    x_int32_t xit_version;

    /**
     * Method: AddSegment
     *        Add a section to be downloaded.
     * Interface Version:
     *        1
     * Implementation Required:
     *        Yes
     * Parameters:
     *        xthis_ptr        -    Pointer to the interface structure itself.
     *        xst_offset        -    The offset of the hint reported to be downloaded.
     *        xst_size        -    The size of the hint reported to be downloaded.
     * Return Value:
     *        None.
     * Comments:
     *        Called by Foxit SDK to report some downloading hints for download manager.
     *        The position and size of section may be not accurate, part of the section might be already available. 
     *        The download manager must deal with that to maximize download efficiency.
     */
    x_void_t (* xfunc_add_segment)(
        struct XFX_DOWNLOADHINTS * xthis_ptr,
        x_size_t xst_offset,
        x_size_t xst_size);
} XFX_DOWNLOADHINTS;

//====================================================================

// #if (defined(_WIN32) || defined(_WIN64))
// #define STDCALL __stdcall
// #else // !defined(WIN32)
// #define STDCALL
// #endif // defined(WIN32)

#define STDCALL

typedef x_void_t   (STDCALL * XPDF_InitLibrary                )();
typedef x_void_t   (STDCALL * XPDF_DestroyLibrary             )();
typedef x_handle_t (STDCALL * XPDF_Avail_Create               )(XFX_FILEAVAIL * xfavail_ptr, XPDF_FILEACCESS * xfaccessor_ptr);
typedef x_int32_t  (STDCALL * XPDF_Avail_IsDocAvail           )(x_handle_t xht_avail, XFX_DOWNLOADHINTS * xhints_ptr);
typedef x_int32_t  (STDCALL * XPDF_Avail_IsLinearized         )(x_handle_t xht_avail);
typedef x_handle_t (STDCALL * XPDF_Avail_GetDocument          )(x_handle_t xht_avail, x_cstring_t xszt_passwd);
typedef x_int32_t  (STDCALL * XPDF_Avail_IsPageAvail          )(x_handle_t xht_avail, x_int32_t xit_page_no, XFX_DOWNLOADHINTS * xhints_ptr);
typedef x_void_t   (STDCALL * XPDF_Avail_Destroy              )(x_handle_t xht_avail);
typedef x_handle_t (STDCALL * XPDF_LoadCustomDocument         )(XPDF_FILEACCESS * xfaccessor_ptr, x_cstring_t xszt_passwd);
typedef x_int32_t  (STDCALL * XPDF_GetPageCount               )(x_handle_t xht_document);
typedef x_void_t   (STDCALL * XPDF_CloseDocument              )(x_handle_t xht_document);
typedef x_handle_t (STDCALL * XPDF_LoadPage                   )(x_handle_t xht_document, x_int32_t xit_page_index);
typedef x_void_t   (STDCALL * XPDF_Page_Delete                )(x_handle_t xht_document, x_int32_t xit_page_index);
typedef x_void_t   (STDCALL * XPDF_ClosePage                  )(x_handle_t xht_page);
typedef x_handle_t (STDCALL * XPDF_Text_LoadPage              )(x_handle_t xht_page);
typedef x_handle_t (STDCALL * XPDF_Text_ClosePage             )(x_handle_t xht_page);
typedef x_lfloat_t (STDCALL * XPDF_GetPageWidth               )(x_handle_t xht_page);
typedef x_lfloat_t (STDCALL * XPDF_GetPageHeight              )(x_handle_t xht_page);
typedef x_int32_t  (STDCALL * XPDF_GetPageSizeByIndex         )(x_handle_t xht_document, x_int32_t xit_page_index, x_lfloat_t * xlft_cx, x_lfloat_t * xlft_cy);
typedef x_handle_t (STDCALL * XPDF_Bitmap_Create              )(x_int32_t xit_cx, x_int32_t xit_cy, x_bool_t xbt_alpha);
typedef x_handle_t (STDCALL * XPDF_Bitmap_CreateEx            )(x_int32_t xit_cx, x_int32_t xit_cy, x_int32_t xit_format, x_pvoid_t xpvt_bits_dptr, x_int32_t xit_stride);
typedef x_void_t   (STDCALL * XPDF_Bitmap_Destroy             )(x_handle_t xht_bitmap);
typedef x_void_t   (STDCALL * XPDF_Bitmap_FillRect            )(x_handle_t xht_bitmap, x_int32_t xit_x, x_int32_t xit_y, x_int32_t xit_cx, x_int32_t xit_cy, x_uint32_t xut_argb);
typedef x_int32_t  (STDCALL * XPDF_Bitmap_GetWidth            )(x_handle_t xht_bitmap);
typedef x_int32_t  (STDCALL * XPDF_Bitmap_GetHeight           )(x_handle_t xht_bitmap);
typedef x_int32_t  (STDCALL * XPDF_Bitmap_GetStride           )(x_handle_t xht_bitmap);
typedef x_handle_t (STDCALL * XPDF_Bitmap_GetBuffer           )(x_handle_t xht_bitmap);
typedef x_void_t   (STDCALL * XPDF_RenderPageBitmap           )(x_handle_t xht_bitmap, x_handle_t xht_page, x_int32_t xit_x, x_int32_t xit_y, x_int32_t xit_cx, x_int32_t xit_cy, x_int32_t xit_rotate, x_int32_t xit_flags);

typedef x_handle_t (STDCALL * XPDF_CreateNewDocument          )();
typedef x_handle_t (STDCALL * XPDF_Page_New                   )(x_handle_t xht_document, x_int32_t xit_page_index, x_lfloat_t xlft_cx, x_lfloat_t xlft_cy);
typedef x_handle_t (STDCALL * XPDF_PageObj_NewImageObj        )(x_handle_t xht_document);
typedef x_bool_t   (STDCALL * XPDF_ImageObj_LoadJpegFileInline)(x_handle_t * xht_pages, x_int32_t xit_count, x_handle_t xht_image_object, XPDF_FILEACCESS * xfaccessor_ptr);
typedef x_bool_t   (STDCALL * XPDF_ImageObj_SetBitmap         )(x_handle_t * xht_pages, x_int32_t xit_count, x_handle_t xht_image_object, x_handle_t xht_bitmap);
typedef x_void_t   (STDCALL * XPDF_Page_InsertObject          )(x_handle_t xht_page, x_handle_t xht_page_obj);
typedef x_void_t   (STDCALL * XPDF_PageObj_Transform          )(x_handle_t xht_page_object, x_lfloat_t xlft_11, x_lfloat_t xlft_21, x_lfloat_t xlft_12, x_lfloat_t xlft_22, x_lfloat_t xlft_13, x_lfloat_t xlft_23);
typedef x_bool_t   (STDCALL * XPDF_ImageObj_SetMatrix         )(x_handle_t xht_image_object, x_lfloat_t xlft_11, x_lfloat_t xlft_21, x_lfloat_t xlft_12, x_lfloat_t xlft_22, x_lfloat_t xlft_13, x_lfloat_t xlft_23);
typedef x_void_t   (STDCALL * XPDF_PageObj_Destroy            )(x_handle_t xht_page_obj);
typedef x_bool_t   (STDCALL * XPDF_Page_GenerateContent       )(x_handle_t xht_page);
typedef x_bool_t   (STDCALL * XPDF_SaveAsCopy                 )(x_handle_t xht_document, XPDF_FILEWRITE * xfilewriter_ptr, x_ulong_t xult_flags);
typedef x_bool_t   (STDCALL * XPDF_SaveWithVersion            )(x_handle_t xht_document, XPDF_FILEWRITE * xfilewriter_ptr, x_ulong_t xult_flags, x_int32_t xit_file_version);

//====================================================================

static x_handle_t                       xht_lib_instance                  = X_NULL;

static XPDF_InitLibrary                 xfunc_InitLibrary                 = X_NULL;
static XPDF_DestroyLibrary              xfunc_DestroyLibrary              = X_NULL;
static XPDF_Avail_Create                xfunc_Avail_Create                = X_NULL;
static XPDF_Avail_IsDocAvail            xfunc_Avail_IsDocAvail            = X_NULL;
static XPDF_Avail_IsLinearized          xfunc_Avail_IsLinearized          = X_NULL;
static XPDF_Avail_GetDocument           xfunc_Avail_GetDocument           = X_NULL;
static XPDF_Avail_IsPageAvail           xfunc_Avail_IsPageAvail           = X_NULL;
static XPDF_Avail_Destroy               xfunc_Avail_Destroy               = X_NULL;
static XPDF_LoadCustomDocument          xfunc_LoadCustomDocument          = X_NULL;
static XPDF_GetPageCount                xfunc_GetPageCount                = X_NULL;
static XPDF_CloseDocument               xfunc_CloseDocument               = X_NULL;
static XPDF_LoadPage                    xfunc_LoadPage                    = X_NULL;
static XPDF_Page_Delete                 xfunc_Page_Delete                 = X_NULL;
static XPDF_ClosePage                   xfunc_ClosePage                   = X_NULL;
static XPDF_Text_LoadPage               xfunc_Text_LoadPage               = X_NULL;
static XPDF_Text_ClosePage              xfunc_Text_ClosePage              = X_NULL;
static XPDF_GetPageWidth                xfunc_GetPageWidth                = X_NULL;
static XPDF_GetPageHeight               xfunc_GetPageHeight               = X_NULL;
static XPDF_GetPageSizeByIndex          xfunc_GetPageSizeByIndex          = X_NULL;
static XPDF_Bitmap_Create               xfunc_Bitmap_Create               = X_NULL;
static XPDF_Bitmap_CreateEx             xfunc_Bitmap_CreateEx             = X_NULL;
static XPDF_Bitmap_Destroy              xfunc_Bitmap_Destroy              = X_NULL;
static XPDF_Bitmap_FillRect             xfunc_Bitmap_FillRect             = X_NULL;
static XPDF_Bitmap_GetWidth             xfunc_Bitmap_GetWidth             = X_NULL;
static XPDF_Bitmap_GetHeight            xfunc_Bitmap_GetHeight            = X_NULL;
static XPDF_Bitmap_GetStride            xfunc_Bitmap_GetStride            = X_NULL;
static XPDF_Bitmap_GetBuffer            xfunc_Bitmap_GetBuffer            = X_NULL;
static XPDF_RenderPageBitmap            xfunc_RenderPageBitmap            = X_NULL;

static XPDF_CreateNewDocument           xfunc_CreateNewDocument           = X_NULL;
static XPDF_Page_New                    xfunc_Page_New                    = X_NULL;
static XPDF_PageObj_NewImageObj         xfunc_PageObj_NewImageObj         = X_NULL;
static XPDF_ImageObj_LoadJpegFileInline xfunc_ImageObj_LoadJpegFileInline = X_NULL;
static XPDF_ImageObj_SetBitmap          xfunc_ImageObj_SetBitmap          = X_NULL;
static XPDF_Page_InsertObject           xfunc_Page_InsertObject           = X_NULL;
static XPDF_PageObj_Transform           xfunc_PageObj_Transform           = X_NULL;
static XPDF_ImageObj_SetMatrix          xfunc_ImageObj_SetMatrix          = X_NULL;
static XPDF_PageObj_Destroy             xfunc_PageObj_Destroy             = X_NULL;
static XPDF_Page_GenerateContent        xfunc_Page_GenerateContent        = X_NULL;
static XPDF_SaveAsCopy                  xfunc_SaveAsCopy                  = X_NULL;
static XPDF_SaveWithVersion             xfunc_SaveWithVersion             = X_NULL;

//====================================================================

/**********************************************************/
/**
 * @brief FPDFium 库的加载操作。
 */
static x_int32_t xpdf_lib_load(x_cstring_t xszt_lib_fname)
{
    x_int32_t xit_err = X_ERR_UNKNOW;

    do 
    {
        //======================================

        if ((X_NULL != xht_lib_instance) || (X_NULL == xszt_lib_fname))
        {
            break;
        }

        //======================================
        // load library

        xht_lib_instance = (x_handle_t)lib_load(xszt_lib_fname);
        if (X_NULL == xht_lib_instance)
        {
            xit_err = last_error();
            break;
        }

        //======================================
        // function table

        xfunc_InitLibrary                 = (XPDF_InitLibrary                )lib_func(xht_lib_instance, "FPDF_InitLibrary"               );
        xfunc_DestroyLibrary              = (XPDF_DestroyLibrary             )lib_func(xht_lib_instance, "FPDF_DestroyLibrary"            );
        xfunc_Avail_Create                = (XPDF_Avail_Create               )lib_func(xht_lib_instance, "FPDFAvail_Create"               );
        xfunc_Avail_IsDocAvail            = (XPDF_Avail_IsDocAvail           )lib_func(xht_lib_instance, "FPDFAvail_IsDocAvail"           );
        xfunc_Avail_IsLinearized          = (XPDF_Avail_IsLinearized         )lib_func(xht_lib_instance, "FPDFAvail_IsLinearized"         );
        xfunc_Avail_GetDocument           = (XPDF_Avail_GetDocument          )lib_func(xht_lib_instance, "FPDFAvail_GetDocument"          );
        xfunc_Avail_IsPageAvail           = (XPDF_Avail_IsPageAvail          )lib_func(xht_lib_instance, "FPDFAvail_IsPageAvail"          );
        xfunc_Avail_Destroy               = (XPDF_Avail_Destroy              )lib_func(xht_lib_instance, "FPDFAvail_Destroy"              );
        xfunc_LoadCustomDocument          = (XPDF_LoadCustomDocument         )lib_func(xht_lib_instance, "FPDF_LoadCustomDocument"        );
        xfunc_GetPageCount                = (XPDF_GetPageCount               )lib_func(xht_lib_instance, "FPDF_GetPageCount"              );
        xfunc_CloseDocument               = (XPDF_CloseDocument              )lib_func(xht_lib_instance, "FPDF_CloseDocument"             );
        xfunc_LoadPage                    = (XPDF_LoadPage                   )lib_func(xht_lib_instance, "FPDF_LoadPage"                  );
        xfunc_Page_Delete                 = (XPDF_Page_Delete                )lib_func(xht_lib_instance, "FPDFPage_Delete"                );
        xfunc_ClosePage                   = (XPDF_ClosePage                  )lib_func(xht_lib_instance, "FPDF_ClosePage"                 );
        xfunc_Text_LoadPage               = (XPDF_Text_LoadPage              )lib_func(xht_lib_instance, "FPDFText_LoadPage"              );
        xfunc_Text_ClosePage              = (XPDF_Text_ClosePage             )lib_func(xht_lib_instance, "FPDFText_ClosePage"             );
        xfunc_GetPageWidth                = (XPDF_GetPageWidth               )lib_func(xht_lib_instance, "FPDF_GetPageWidth"              );
        xfunc_GetPageHeight               = (XPDF_GetPageHeight              )lib_func(xht_lib_instance, "FPDF_GetPageHeight"             );
        xfunc_GetPageSizeByIndex          = (XPDF_GetPageSizeByIndex         )lib_func(xht_lib_instance, "FPDF_GetPageSizeByIndex"        );
        xfunc_Bitmap_Create               = (XPDF_Bitmap_Create              )lib_func(xht_lib_instance, "FPDFBitmap_Create"              );
        xfunc_Bitmap_CreateEx             = (XPDF_Bitmap_CreateEx            )lib_func(xht_lib_instance, "FPDFBitmap_CreateEx"            );
        xfunc_Bitmap_Destroy              = (XPDF_Bitmap_Destroy             )lib_func(xht_lib_instance, "FPDFBitmap_Destroy"             );
        xfunc_Bitmap_FillRect             = (XPDF_Bitmap_FillRect            )lib_func(xht_lib_instance, "FPDFBitmap_FillRect"            );
        xfunc_Bitmap_GetWidth             = (XPDF_Bitmap_GetWidth            )lib_func(xht_lib_instance, "FPDFBitmap_GetWidth"            );
        xfunc_Bitmap_GetHeight            = (XPDF_Bitmap_GetHeight           )lib_func(xht_lib_instance, "FPDFBitmap_GetHeight"           );
        xfunc_Bitmap_GetStride            = (XPDF_Bitmap_GetStride           )lib_func(xht_lib_instance, "FPDFBitmap_GetStride"           );
        xfunc_Bitmap_GetBuffer            = (XPDF_Bitmap_GetBuffer           )lib_func(xht_lib_instance, "FPDFBitmap_GetBuffer"           );
        xfunc_RenderPageBitmap            = (XPDF_RenderPageBitmap           )lib_func(xht_lib_instance, "FPDF_RenderPageBitmap"          );

        xfunc_CreateNewDocument           = (XPDF_CreateNewDocument          )lib_func(xht_lib_instance, "FPDF_CreateNewDocument"         );
        xfunc_Page_New                    = (XPDF_Page_New                   )lib_func(xht_lib_instance, "FPDFPage_New"                   );
        xfunc_PageObj_NewImageObj         = (XPDF_PageObj_NewImageObj        )lib_func(xht_lib_instance, "FPDFPageObj_NewImageObj"        );
        xfunc_ImageObj_LoadJpegFileInline = (XPDF_ImageObj_LoadJpegFileInline)lib_func(xht_lib_instance, "FPDFImageObj_LoadJpegFileInline");
        xfunc_ImageObj_SetBitmap          = (XPDF_ImageObj_SetBitmap         )lib_func(xht_lib_instance, "FPDFImageObj_SetBitmap"         );
        xfunc_Page_InsertObject           = (XPDF_Page_InsertObject          )lib_func(xht_lib_instance, "FPDFPage_InsertObject"          );
        xfunc_PageObj_Transform           = (XPDF_PageObj_Transform          )lib_func(xht_lib_instance, "FPDFPageObj_Transform"          );
        xfunc_ImageObj_SetMatrix          = (XPDF_ImageObj_SetMatrix         )lib_func(xht_lib_instance, "FPDFImageObj_SetMatrix"         );
        xfunc_PageObj_Destroy             = (XPDF_PageObj_Destroy            )lib_func(xht_lib_instance, "FPDFPageObj_Destroy"            );
        xfunc_Page_GenerateContent        = (XPDF_Page_GenerateContent       )lib_func(xht_lib_instance, "FPDFPage_GenerateContent"       );
        xfunc_SaveAsCopy                  = (XPDF_SaveAsCopy                 )lib_func(xht_lib_instance, "FPDF_SaveAsCopy"                );
        xfunc_SaveWithVersion             = (XPDF_SaveWithVersion            )lib_func(xht_lib_instance, "FPDF_SaveWithVersion"           );

#define XFUNC_CHECK(exp)  if (!(exp)) { XASSERT(exp); xit_err = X_ERR_UNKNOW; break; }

        XFUNC_CHECK(X_NULL != xfunc_InitLibrary                )
        XFUNC_CHECK(X_NULL != xfunc_DestroyLibrary             )
        XFUNC_CHECK(X_NULL != xfunc_Avail_Create               )
        XFUNC_CHECK(X_NULL != xfunc_Avail_IsDocAvail           )
        XFUNC_CHECK(X_NULL != xfunc_Avail_IsLinearized         )
        XFUNC_CHECK(X_NULL != xfunc_Avail_GetDocument          )
        XFUNC_CHECK(X_NULL != xfunc_Avail_IsPageAvail          )
        XFUNC_CHECK(X_NULL != xfunc_Avail_Destroy              )
        XFUNC_CHECK(X_NULL != xfunc_LoadCustomDocument         )
        XFUNC_CHECK(X_NULL != xfunc_GetPageCount               )
        XFUNC_CHECK(X_NULL != xfunc_CloseDocument              )
        XFUNC_CHECK(X_NULL != xfunc_LoadPage                   )
        XFUNC_CHECK(X_NULL != xfunc_Page_Delete                )
        XFUNC_CHECK(X_NULL != xfunc_ClosePage                  )
        XFUNC_CHECK(X_NULL != xfunc_Text_LoadPage              )
        XFUNC_CHECK(X_NULL != xfunc_Text_ClosePage             )
        XFUNC_CHECK(X_NULL != xfunc_GetPageWidth               )
        XFUNC_CHECK(X_NULL != xfunc_GetPageHeight              )
        XFUNC_CHECK(X_NULL != xfunc_GetPageSizeByIndex         )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_Create              )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_CreateEx            )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_Destroy             )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_FillRect            )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_GetWidth            )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_GetHeight           )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_GetStride           )
        XFUNC_CHECK(X_NULL != xfunc_Bitmap_GetBuffer           )
        XFUNC_CHECK(X_NULL != xfunc_RenderPageBitmap           )

        XFUNC_CHECK(X_NULL != xfunc_CreateNewDocument          )
        XFUNC_CHECK(X_NULL != xfunc_Page_New                   )
        XFUNC_CHECK(X_NULL != xfunc_PageObj_NewImageObj        )
        XFUNC_CHECK(X_NULL != xfunc_ImageObj_LoadJpegFileInline)
        XFUNC_CHECK(X_NULL != xfunc_ImageObj_SetBitmap         )
        XFUNC_CHECK(X_NULL != xfunc_Page_InsertObject          )
        XFUNC_CHECK(X_NULL != xfunc_PageObj_Transform          )
        XFUNC_CHECK(X_NULL != xfunc_ImageObj_SetMatrix         )
        XFUNC_CHECK(X_NULL != xfunc_PageObj_Destroy            )
        XFUNC_CHECK(X_NULL != xfunc_Page_GenerateContent       )
        XFUNC_CHECK(X_NULL != xfunc_SaveAsCopy                 )
        XFUNC_CHECK(X_NULL != xfunc_SaveWithVersion            )

#undef  XFUNC_CHECK

        //======================================
        xit_err = X_ERR_OK;
    } while (0);

    return xit_err;
}

/**********************************************************/
/**
 * @brief FPDFium 库的卸载操作。
 */
static x_void_t xpdf_lib_unload()
{
    if (X_NULL != xht_lib_instance)
    {
        xfunc_InitLibrary                 = X_NULL;
        xfunc_DestroyLibrary              = X_NULL;
        xfunc_Avail_Create                = X_NULL;
        xfunc_Avail_IsDocAvail            = X_NULL;
        xfunc_Avail_IsLinearized          = X_NULL;
        xfunc_Avail_GetDocument           = X_NULL;
        xfunc_Avail_IsPageAvail           = X_NULL;
        xfunc_Avail_Destroy               = X_NULL;
        xfunc_LoadCustomDocument          = X_NULL;
        xfunc_GetPageCount                = X_NULL;
        xfunc_CloseDocument               = X_NULL;
        xfunc_LoadPage                    = X_NULL;
        xfunc_Page_Delete                 = X_NULL;
        xfunc_ClosePage                   = X_NULL;
        xfunc_Text_LoadPage               = X_NULL;
        xfunc_Text_ClosePage              = X_NULL;
        xfunc_GetPageWidth                = X_NULL;
        xfunc_GetPageHeight               = X_NULL;
        xfunc_GetPageSizeByIndex          = X_NULL;
        xfunc_Bitmap_Create               = X_NULL;
        xfunc_Bitmap_CreateEx             = X_NULL;
        xfunc_Bitmap_Destroy              = X_NULL;
        xfunc_Bitmap_FillRect             = X_NULL;
        xfunc_Bitmap_GetWidth             = X_NULL;
        xfunc_Bitmap_GetHeight            = X_NULL;
        xfunc_Bitmap_GetStride            = X_NULL;
        xfunc_Bitmap_GetBuffer            = X_NULL;
        xfunc_RenderPageBitmap            = X_NULL;

        xfunc_CreateNewDocument           = X_NULL;
        xfunc_Page_New                    = X_NULL;
        xfunc_PageObj_NewImageObj         = X_NULL;
        xfunc_ImageObj_LoadJpegFileInline = X_NULL;
        xfunc_ImageObj_SetBitmap          = X_NULL;
        xfunc_Page_InsertObject           = X_NULL;
        xfunc_PageObj_Transform           = X_NULL;
        xfunc_ImageObj_SetMatrix          = X_NULL;
        xfunc_PageObj_Destroy             = X_NULL;
        xfunc_Page_GenerateContent        = X_NULL;
        xfunc_SaveAsCopy                  = X_NULL;
        xfunc_SaveWithVersion             = X_NULL;

        lib_unload(xht_lib_instance);
        xht_lib_instance = X_NULL;
    }
}

//====================================================================

/**********************************************************/
/**
 * @brief 文件读取操作的回调函数接口。
 */
static x_int32_t read_file_block(
                        x_pvoid_t xpvt_param,
                        x_ulong_t xut_position,
                        x_uchar_t * xct_buffer,
                        x_ulong_t xut_size)
{
    if (0 !=  fseek((FILE *)xpvt_param, (long)xut_position, SEEK_SET))
    {
        return X_FALSE;
    }

    if (xut_size !=
            fread(
                xct_buffer,
                sizeof(x_uchar_t),
                xut_size,
                (FILE *)xpvt_param)
       )
    {
        return X_FALSE;
    }

    return X_TRUE;
}

/**********************************************************/
/**
 * @brief 文件写入操作的回调函数接口。
 */
x_int32_t write_file_block(
                struct XPDF_FILEWRITE * xfw_optr,
                const x_pvoid_t xpvt_dptr,
                x_ulong_t xut_size)
{
    if (xut_size !=
            fwrite(
                xpvt_dptr,
                sizeof(x_uchar_t),
                xut_size,
                (FILE *)xfw_optr->xht_param)
       )
    {
        return -1;
    }

    return (x_int32_t)xut_size;
}

/**********************************************************/
/**
 * Method: is_data_avail
 *        Report whether the specified data section is available.
 *      A section is available only if all bytes in the section is available. 
 * Interface Version:
 *        1
 * Implementation Required:
 *        Yes
 * Parameters:
 *        this_ptr        -    Pointer to the interface structure itself.
 *        xst_offset        -    The offset of the data section in the file.
 *        xst_size        -    The size of the data section
 * Return Value:
 *        true means the specified data section is available.
 * Comments:
 *        Called by Foxit SDK to check whether the data section is ready.
 */
static x_uchar_t is_data_avail(
                    struct XFX_FILEAVAIL * this_ptr,
                    x_size_t xst_offset,
                    x_size_t xst_size)
{
    return X_TRUE;
}

/**********************************************************/
/**
 * Method: add_segment
 *        Add a section to be downloaded.
 * Interface Version:
 *        1
 * Implementation Required:
 *        Yes
 * Parameters:
 *        this_ptr        -    Pointer to the interface structure itself.
 *        xst_offset        -    The offset of the hint reported to be downloaded.
 *        xst_size        -    The size of the hint reported to be downloaded.
 * Return Value:
 *        None.
 * Comments:
 *        Called by Foxit SDK to report some downloading hints for download manager.
 *        The position and size of section may be not accurate, part of the section might be already available. 
 *        The download manager must deal with that to maximize download efficiency.
 */
x_void_t add_segment(
            struct XFX_DOWNLOADHINTS * this_ptr,
            x_size_t xst_offset,
            x_size_t xst_size)
{

}

/**********************************************************/
/**
 * @brief 打开文件访问器。
 * @note  返回 X_NULL 时，可使用 last_error() 获知错误码。
 */
x_handle_t file_open_accessor(x_cstring_t xszt_filename)
{
    x_handle_t        xht_file       = X_NULL;
    XPDF_FILEACCESS * xfaccessor_ptr = X_NULL;

    xht_file = (x_handle_t)fopen(xszt_filename, "rb");
    if (X_NULL == xht_file)
    {
        return X_NULL;
    }

    xfaccessor_ptr = (XPDF_FILEACCESS *)calloc(1, sizeof(XPDF_FILEACCESS));
    if (X_NULL == xfaccessor_ptr)
    {
        fclose((FILE *)xht_file);
        return X_NULL;
    }

    fseek((FILE *)xht_file, 0, SEEK_END);

    xfaccessor_ptr->xut_flen         = (x_ulong_t)ftell((FILE *)xht_file);
    xfaccessor_ptr->xfunc_read_block = read_file_block;
    xfaccessor_ptr->xht_param        = xht_file;

    rewind((FILE *)xht_file);

    return (x_handle_t)xfaccessor_ptr;
}

/**********************************************************/
/**
 * @brief 关闭文件访问器。
 */
x_void_t file_close_accessor(x_handle_t xht_faccessor)
{
    if (X_NULL != xht_faccessor)
    {
        fclose((FILE *)((XPDF_FILEACCESS *)xht_faccessor)->xht_param);
        free(xht_faccessor);

        xht_faccessor = X_NULL;
    }
}

/**********************************************************/
/**
 * @brief 打开文件写入器。
 */
x_handle_t file_open_writer(x_cstring_t xszt_filename)
{
    x_handle_t       xht_file     = X_NULL;
    XPDF_FILEWRITE * xfwriter_ptr = X_NULL;

    xht_file = (x_handle_t)fopen(xszt_filename, "wb+");
    if (X_NULL == xht_file)
    {
        return X_NULL;
    }

    xfwriter_ptr = (XPDF_FILEWRITE *)calloc(1, sizeof(XPDF_FILEWRITE));
    if (X_NULL == xfwriter_ptr)
    {
        fclose((FILE *)xht_file);
        return X_NULL;
    }

    xfwriter_ptr->xit_version       = 0;
    xfwriter_ptr->xfunc_write_block = write_file_block;
    xfwriter_ptr->xht_param         = xht_file;

    return (x_handle_t)xfwriter_ptr;
}

/**********************************************************/
/**
 * @brief 关闭文件写入器。
 */
x_void_t file_close_writer(x_handle_t xht_fwriter)
{
    if (X_NULL != xht_fwriter)
    {
        fclose((FILE *)((XPDF_FILEWRITE *)xht_fwriter)->xht_param);
        free(xht_fwriter);

        xht_fwriter = X_NULL;
    }
}

/**********************************************************/
/**
 * @brief
 * 保持纵横比例（maintain aspect ratio）的情况下，
 * 在限定的最大尺寸值（宽高）中，计算出缩放后的最大尺寸值（宽高）。
 * 
 * @note xut_mcx 和 xut_mcy 同时为 0 时，xut_rcx 和 xut_rcy 值不做修改。
 * 
 * @param [in,out] xut_rcx : 入参，原尺寸宽度；回参，操作成功返回的建议宽度。
 * @param [in,out] xut_rcy : 入参，原尺寸高度；回参，操作成功返回的建议高度。
 * @param [in    ] xut_mcx : 限定的最大尺寸宽度。为 0 时，以 xut_mcy 为准。
 * @param [in    ] xut_mcy : 限定的最大尺寸高度。为 0 时，以 xut_mcx 为准。
 * 
 * @return x_bool_t
 *         - 成功，返回 X_TRUE；
 *         - 失败，返回 X_FALSE。
 */
x_bool_t mar_resize(
                x_uint32_t & xut_rcx,
                x_uint32_t & xut_rcy,
                x_uint32_t   xut_mcx,
                x_uint32_t   xut_mcy)
{
#define __CALC_USE_OPTIMIZED_IMPL__ 1

#if __CALC_USE_OPTIMIZED_IMPL__
    x_float_t xft_mrx = 0.0F;
    x_float_t xft_mry = 0.0F;
#else // !__CALC_USE_OPTIMIZED_IMPL__
    x_float_t xft_mrf = 1.0F;
#endif // __CALC_USE_OPTIMIZED_IMPL__

    if ((xut_rcx <= 0) || (xut_rcy <= 0))
    {
        return X_FALSE;
    }

#if __CALC_USE_OPTIMIZED_IMPL__

    if (0 != xut_mcx)
    {
        if (0 != xut_mcy)
        {
            xft_mrx = xut_mcx / (x_float_t)(xut_rcx);
            xft_mry = xut_mcy / (x_float_t)(xut_rcy);

            if ((xft_mrx - xft_mry) > 1e-6F)
            {
                xut_rcx = (x_uint32_t)(xut_rcx * xft_mry);
                xut_rcy = xut_mcy;
            }
            else
            {
                xut_rcx = xut_mcx;
                xut_rcy = (x_uint32_t)(xut_rcy * xft_mrx);
            }
        }
        else
        {
            xut_rcx = xut_mcx;
            xut_rcy = (x_uint32_t)(xut_rcy * xut_mcx / (x_float_t)(xut_rcx));
        }
    }
    else
    {
        if (0 != xut_mcy)
        {
            xut_rcx = (x_uint32_t)(xut_rcx * xut_mcy / (x_float_t)(xut_rcy));
            xut_rcy = xut_mcy;
        }
    }

#else // !__CALC_USE_OPTIMIZED_IMPL__

    if ((0 != xut_mcx) && (0 != xut_mcy))
        xft_mrf =
            fminf(xut_mcx / (x_float_t)(xut_rcx),
                  xut_mcy / (x_float_t)(xut_rcy));
    else if ((0 != xut_mcx) && (0 == xut_mcy))
        xft_mrf = xut_mcx / (x_float_t)(xut_rcx);
    else if ((0 == xut_mcx) && (0 != xut_mcy))
        xft_mrf = xut_mcy / (x_float_t)(xut_rcy);
    else
        return X_TRUE;

    xut_rcx = (x_uint32_t)(xut_rcx * xft_mrf);
    xut_rcy = (x_uint32_t)(xut_rcy * xft_mrf);

#endif // __CALC_USE_OPTIMIZED_IMPL__

#undef __CALC_USE_OPTIMIZED_IMPL__

    return X_TRUE;
}

////////////////////////////////////////////////////////////////////////////////

/**********************************************************/
/**
 * @brief 加载 PDFium 库的操作接口。
 * 
 * @param [in ] xszt_lib_path : PDFium 动态库的文件路径名。
 * @param [in ] xpvt_reserved : 保留参数（设置为 X_NULL）。
 * 
 * @return x_int32_t 
 *         - 成功，返回 0；
 *         - 失败，返回 错误码。
 */
x_int32_t XPDF_load(x_cstring_t xszt_lib_path, x_pvoid_t xpvt_reserved)
{
    x_int32_t xit_err = xpdf_lib_load(xszt_lib_path);
    if (0 != xit_err)
    {
        return xit_err;
    }

    xfunc_InitLibrary();

    return xit_err;
}

/**********************************************************/
/**
 * @brief 反初始化（卸载） PDFium 库的操作接口。
 */
x_void_t XPDF_unload(void)
{
    if (X_NULL != xht_lib_instance)
    {
        xfunc_DestroyLibrary();
        xpdf_lib_unload();
    }
}

////////////////////////////////////////////////////////////////////////////////
// XPDF_reader_t

//====================================================================

// 
// XPDF_reader_t : constructor/destuctor
// 

XPDF_reader_t::XPDF_reader_t(void)
    : m_xht_faccess(X_NULL)
    , m_xht_favalid(X_NULL)
    , m_xht_fdhinst(X_NULL)
    , m_xht_fdocument(X_NULL)
    , m_xht_xavalid(X_NULL)
    , m_xit_page_count(0)
    , m_xht_bitmap(X_NULL)
    , m_xut_bmpcx(0)
    , m_xut_bmpcy(0)
{

}

XPDF_reader_t::XPDF_reader_t::~XPDF_reader_t(void)
{
    close();
}

//====================================================================

// 
// XPDF_reader_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 打开操作。
 * 
 * @param [in ] xszt_path   : PDF 文件路径。
 * @param [in ] xszt_passwd : 文件密码（仅对加密的 PDF 文件设置）。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_reader_t::XPDF_reader_t::open(
            x_cstring_t xszt_path,
            x_cstring_t xszt_passwd)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    x_bool_t xbt_open = X_FALSE;

    do 
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_OPEN_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (is_open())
        {
            xerr_no = XERR_MAKE(X_ERR_OPEN_ALREADY_OPENED, 0);
            break;
        }

        if (X_NULL == xszt_path)
        {
            xerr_no = XERR_MAKE(X_ERR_OPEN_PARAM_INVALID, 0);
            break;
        }

        //======================================
        // 打开文件（构建文件访问器），并初始化相关操作句柄

        xbt_open = X_TRUE;

        m_xht_faccess = file_open_accessor(xszt_path);
        if (X_NULL == m_xht_faccess)
        {
            xerr_no = XERR_MAKE(X_ERR_OPEN_FILE_OPEN_ACCESSOR, last_error());
            break;
        }

        xerr_no = doc_load(xszt_passwd);
        if (XERR_FAILED(xerr_no))
        {
            break;
        }

        m_sz_filename = xszt_path;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (XERR_FAILED(xerr_no) && xbt_open)
    {
        close();
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 关闭操作。
 */
x_void_t XPDF_reader_t::XPDF_reader_t::close(void)
{
    if (X_NULL == xht_lib_instance)
        return;

    m_sz_filename = "";

    if (X_NULL != m_xht_faccess)
    {
        doc_free();
        file_close_accessor(m_xht_faccess);
        m_xht_faccess = X_NULL;
    }

    if (X_NULL != m_xht_bitmap)
    {
        xfunc_Bitmap_Destroy(m_xht_bitmap);
        m_xht_bitmap = X_NULL;
    }

    m_xut_bmpcx = 0;
    m_xut_bmpcy = 0;
}

/**********************************************************/
/**
 * @brief 获取页面尺寸。
 * 
 * @param [in ] xit_page_no : 页面序号（起始序号为 0）。
 * @param [out] xlft_cx     : 操作成功返回的页面宽度。
 * @param [out] xlft_cy     : 操作成功返回的页面高度。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_reader_t::get_page_size(
            x_int32_t xit_page_no,
            x_lfloat_t & xlft_cx,
            x_lfloat_t & xlft_cy) const
{
    if (X_NULL == xht_lib_instance)
    {
        return XERR_MAKE(X_ERR_GPZ_PDFLIB_NOT_LOADED, 0);
    }

    if (!is_open())
    {
        return XERR_MAKE(X_ERR_GPZ_UNOPENED, 0);
    }

    if (X_NULL == m_xht_fdocument)
    {
        return XERR_MAKE(X_ERR_GPZ_DOC_ISNULL, 0);
    }

    if ((xit_page_no < 0) || (xit_page_no >= get_page_count()))
    {
        return XERR_MAKE(X_ERR_GPZ_PARAM_INVALID, 0);
    }

    if (0 == xfunc_GetPageSizeByIndex(
                        m_xht_fdocument,
                        xit_page_no,
                        &xlft_cx,
                        &xlft_cy))
    {
        return XERR_MAKE(X_ERR_GPZ_XFUNC_PAGESIZE, 0);
    }

    return X_ERR_OK;
}

/**********************************************************/
/**
 * @brief 获取页面尺寸。
 * 
 * @param [in ] xit_page_no : 页面序号（起始序号为 0）。
 * @param [out] xlft_cx     : 操作成功返回的页面宽度。
 * @param [out] xlft_cy     : 操作成功返回的页面高度。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_reader_t::get_page_size_ex(
            x_int32_t xit_page_no,
            x_lfloat_t & xlft_cx,
            x_lfloat_t & xlft_cy)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    x_handle_t xht_page = X_NULL;

    do
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_GPZEX_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (!is_open())
        {
            xerr_no = XERR_MAKE(X_ERR_GPZEX_UNOPENED, 0);
            break;
        }

        if ((xit_page_no < 0) ||
            (xit_page_no >= get_page_count()))
        {
            xerr_no = XERR_MAKE(X_ERR_GPZEX_PARAM_INVALID, 0);
            break;
        }

        //======================================

        xerr_no = page_load(xht_page, xit_page_no);
        if (XERR_FAILED(xerr_no))
        {
            break;
        }

        //======================================

        xlft_cx = xfunc_GetPageWidth(xht_page);
        xlft_cy = xfunc_GetPageHeight(xht_page);

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xht_page)
    {
        page_free(xht_page);
        xht_page = X_NULL;
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 渲染指定的页面。
 * 
 * @param [in    ] xit_page_no : 页面序号（起始序号为 0）。
 * @param [in    ] xft_pgzoom  : 页面宽高的缩放倍数（如可取 1.0F，2.0F 等值）。
 * @param [in,out] xut_cx      : 页面宽度（入参，表示页面渲染操作的最大宽度，
 *                               为 0 时，取实际缩放后宽度；
 *                               回参，渲染操作返回的实际图像宽度）。
 * @param [in,out] xut_cy      : 页面高度（入参，表示页面渲染操作的最大高度，
 *                               为 0 时，取实际缩放后高度；
 *                               回参，渲染操作返回的实际图像高度）。
 * @param [out   ] xbits_ptr   : 操作成功返回的图像像素数据缓存地址。
 * @param [out   ] xit_pitch   : 操作成功返回的图像行扫描（字节）步进长度。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_reader_t::render_page(
                x_int32_t xit_page_no,
                x_float_t xft_pgzoom,
                x_uint32_t & xut_cx,
                x_uint32_t & xut_cy,
                x_pvoid_t & xbits_ptr,
                x_int32_t & xit_pitch)
{
    x_errno_t  xerr_no     = X_ERR_UNKNOW;
    x_handle_t xht_page    = X_NULL;
    x_uint32_t xut_page_cx = 0;
    x_uint32_t xut_page_cy = 0;

    do 
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_RENDER_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (!is_open())
        {
            xerr_no = XERR_MAKE(X_ERR_RENDER_UNOPENED, 0);
            break;
        }

        if ((xit_page_no < 0) ||
            (xit_page_no >= get_page_count()))
        {
            xerr_no = XERR_MAKE(X_ERR_RENDER_PARAM_INVALID, 0);
            break;
        }

        //======================================

        xerr_no = page_load(xht_page, xit_page_no);
        if (XERR_FAILED(xerr_no))
        {
            break;
        }

        //======================================

        xut_page_cx = (x_uint32_t)(xft_pgzoom * xfunc_GetPageWidth(xht_page));
        xut_page_cy = (x_uint32_t)(xft_pgzoom * xfunc_GetPageHeight(xht_page));
        if (!mar_resize(xut_page_cx, xut_page_cy, xut_cx, xut_cy))
        {
            xerr_no = XERR_MAKE(X_ERR_RENDER_CALC_PAGESIZE, 0);
            break;
        }

        xut_cx = (xut_page_cx + 3) & (~3);   // 保证页面宽度为 4 的倍数
        xut_cy = xut_page_cy;

        xerr_no = auto_resize_bitmap(xut_cx, xut_cy);
        if (XERR_FAILED(xerr_no))
        {
            break;
        }

        //======================================

        xfunc_Bitmap_FillRect(
                    m_xht_bitmap,
                    0,
                    0,
                    (x_int32_t)xut_cx,
                    (x_int32_t)xut_cy,
                    0xFFFFFFFF);

        xfunc_RenderPageBitmap(
                    m_xht_bitmap,
                    xht_page,
                    0,
                    0,
                    (x_int32_t)xut_cx,
                    (x_int32_t)xut_cy,
                    0,
                    0);

        //======================================

        xit_pitch = xfunc_Bitmap_GetStride(m_xht_bitmap);
        xbits_ptr = xfunc_Bitmap_GetBuffer(m_xht_bitmap);

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xht_page)
    {
        page_free(xht_page);
        xht_page = X_NULL;
    }

    return xerr_no;
}

//====================================================================

// 
// XPDF_reader_t : inner invoking
// 

/**********************************************************/
/**
 * @brief 加载文件其他相关的操作句柄。
 */
x_errno_t XPDF_reader_t::doc_load(x_cstring_t xszt_passwd)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    XPDF_FILEACCESS   * xfaccessor_ptr  = (XPDF_FILEACCESS *)m_xht_faccess;

    XFX_FILEAVAIL     * xfavail_ptr     = X_NULL;
    XFX_DOWNLOADHINTS * xfdhinst_ptr    = X_NULL;
    x_handle_t          xfdocument_pptr = X_NULL;
    x_handle_t          xfavail_pptr    = X_NULL;

    XASSERT(X_NULL != xfaccessor_ptr);

    do 
    {
        //======================================

        xfavail_ptr = (XFX_FILEAVAIL *)calloc(1, sizeof(XFX_FILEAVAIL));
        XASSERT (X_NULL != xfavail_ptr);

        xfavail_ptr->xit_version         = 1;
        xfavail_ptr->xfunc_is_data_avail = is_data_avail;

        //======================================

        xfdhinst_ptr = (XFX_DOWNLOADHINTS *)calloc(1, sizeof(XFX_DOWNLOADHINTS));
        XASSERT (X_NULL != xfdhinst_ptr);

        xfdhinst_ptr->xit_version       = 1;
        xfdhinst_ptr->xfunc_add_segment = add_segment;

        //======================================

        xfavail_pptr = xfunc_Avail_Create(xfavail_ptr, xfaccessor_ptr);
        if (X_NULL == xfavail_pptr)
        {
            xerr_no = XERR_MAKE(X_ERR_FLOAD_XFUNC_AVAIL_CREATE, 0);
            break;
        }

        x_int32_t xit_err = xfunc_Avail_IsDocAvail(xfavail_pptr, xfdhinst_ptr);
        if (xit_err <= 0)
        {
            xerr_no = XERR_MAKE(X_ERR_FLOAD_XFUNC_AVAIL_ISDOCAVAIL, 0);
            break;
        }

        //======================================

        if (xfunc_Avail_IsLinearized(xfavail_pptr))
        {
            xfdocument_pptr = xfunc_Avail_GetDocument(xfavail_pptr, xszt_passwd);
        }
        else
        {
            xfdocument_pptr = xfunc_LoadCustomDocument(xfaccessor_ptr, xszt_passwd);
        }

        if (X_NULL == xfdocument_pptr)
        {
            xerr_no = XERR_MAKE(X_ERR_FLOAD_PDFLOAD_DOCUMENT, 0);
            break;
        }

        //======================================

        m_xit_page_count = xfunc_GetPageCount(xfdocument_pptr);
#if 0
        FPDF_GetDocPermissions(xfdocument_pptr);
        FPDFAvail_IsFormAvail(xfavail_pptr, xfdhinst_ptr);

        x_int32_t xit_first_page = FPDFAvail_GetFirstPageNum(xfdocument_pptr);
        FPDFAvail_IsPageAvail(xfavail_pptr, xit_first_page, xfdhinst_ptr);

        for (x_int32_t xit_iter = 0; xit_iter < m_xit_page_count; ++xit_iter)
        {
            FPDFAvail_IsPageAvail(xfavail_pptr, xit_iter, xfdhinst_ptr);
        }
#endif

        //======================================

        m_xht_favalid   = (x_handle_t)xfavail_ptr    ;
        m_xht_fdhinst   = (x_handle_t)xfdhinst_ptr   ;
        m_xht_fdocument = (x_handle_t)xfdocument_pptr;
        m_xht_xavalid   = (x_handle_t)xfavail_pptr   ;

        xfavail_ptr     = X_NULL;
        xfdhinst_ptr    = X_NULL;
        xfdocument_pptr = X_NULL;
        xfavail_pptr    = X_NULL;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    //======================================

    if (X_NULL != xfdocument_pptr)
    {
        xfunc_CloseDocument(xfdocument_pptr);
        xfdocument_pptr = X_NULL;
    }

    if (X_NULL != xfavail_pptr)
    {
        xfunc_Avail_Destroy(xfavail_pptr);
        xfavail_pptr = X_NULL;
    }

    if (X_NULL != xfdhinst_ptr)
    {
        free(xfdhinst_ptr);
        xfdhinst_ptr = X_NULL;
    }

    if (X_NULL != xfavail_ptr)
    {
        free(xfavail_ptr);
        xfavail_ptr = X_NULL;
    }

    //======================================

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 释放文件其他相关的操作句柄。
 */
x_void_t XPDF_reader_t::doc_free(void)
{
    //======================================

    XFX_FILEAVAIL     * xfavail_ptr     = (XFX_FILEAVAIL     *)m_xht_favalid  ;
    XFX_DOWNLOADHINTS * xfdhinst_ptr    = (XFX_DOWNLOADHINTS *)m_xht_fdhinst  ;
    x_handle_t          xfdocument_pptr = (x_handle_t         )m_xht_fdocument;
    x_handle_t          xfavail_pptr    = (x_handle_t         )m_xht_xavalid  ;

    //======================================

    if (X_NULL != xfdocument_pptr)
    {
        xfunc_CloseDocument(xfdocument_pptr);
        xfdocument_pptr = X_NULL;
    }

    if (X_NULL != xfavail_pptr)
    {
        xfunc_Avail_Destroy(xfavail_pptr);
        xfavail_pptr = X_NULL;
    }

    if (X_NULL != xfdhinst_ptr)
    {
        free(xfdhinst_ptr);
        xfdhinst_ptr = X_NULL;
    }

    if (X_NULL != xfavail_ptr)
    {
        free(xfavail_ptr);
        xfavail_ptr = X_NULL;
    }

    //======================================

    m_xht_favalid    = X_NULL;
    m_xht_fdhinst    = X_NULL;
    m_xht_fdocument  = X_NULL;
    m_xht_xavalid    = X_NULL;

    m_xit_page_count = 0;

    //======================================
}

/**********************************************************/
/**
 * @brief 加载页面。
 */
x_errno_t XPDF_reader_t::page_load(
                            x_handle_t & xht_page,
                            x_int32_t xit_page_no)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    xht_page = X_NULL;

    //======================================

    XASSERT((xit_page_no >= 0) && (xit_page_no < get_page_count()));

    //======================================

    do
    {
        //======================================

        if (0 == xfunc_Avail_IsPageAvail(
                        m_xht_xavalid,
                        xit_page_no,
                        (XFX_DOWNLOADHINTS *)m_xht_fdhinst))
        {
            xerr_no = XERR_MAKE(X_ERR_PLOAD_PAGE_INAVAIL, 0);
            break;
        }

        //======================================

        XASSERT(X_NULL != m_xht_fdocument);

        xht_page = xfunc_LoadPage(m_xht_fdocument, xit_page_no);
        if (X_NULL == xht_page)
        {
            xerr_no = XERR_MAKE(X_ERR_PLOAD_PAGE_LDFAILED, 0);
            break;
        }

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    //======================================

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 释放页面。
 */
x_void_t XPDF_reader_t::page_free(x_handle_t xht_page)
{
    if (X_NULL != xht_page)
    {
        xfunc_ClosePage(xht_page);
        xht_page = X_NULL;
    }
}

/**********************************************************/
/**
 * @brief 自动调整 渲染操作使用的缓存位图对象 的宽高。
 */
x_errno_t XPDF_reader_t::auto_resize_bitmap(
                            x_uint32_t xut_cx,
                            x_uint32_t xut_cy)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    do 
    {
        //======================================

        if ((X_NULL != m_xht_bitmap) &&
            (m_xut_bmpcx >= xut_cx ) &&
            (m_xut_bmpcy >= xut_cy ))
        {
            xerr_no = X_ERR_OK;
            break;
        }

        //======================================

        if (X_NULL != m_xht_bitmap)
        {
            xfunc_Bitmap_Destroy(m_xht_bitmap);
            m_xht_bitmap = X_NULL;
        }

        m_xut_bmpcx  = 0;
        m_xut_bmpcy  = 0;

        //======================================

        m_xht_bitmap = xfunc_Bitmap_Create(xut_cx + 128, xut_cy + 64, 0);
        if (X_NULL == m_xht_bitmap)
        {
            xerr_no = XERR_MAKE(X_ERR_ARBMP_XFUNC_BITMAP_CREATE, 0);
            break;
        }

        m_xut_bmpcx = xfunc_Bitmap_GetWidth(m_xht_bitmap);
        m_xut_bmpcy = xfunc_Bitmap_GetHeight(m_xht_bitmap);

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    return xerr_no;
}

////////////////////////////////////////////////////////////////////////////////
// XPDF_writer_t

//====================================================================

// 
// XPDF_writer_t : constructor/destuctor
// 

XPDF_writer_t::XPDF_writer_t(void)
    : m_xht_fdocument(X_NULL)
{

}

XPDF_writer_t::~XPDF_writer_t(void)
{
    destroy();
}

//====================================================================

// 
// XPDF_writer_t : public interfaces
// 

/**********************************************************/
/**
 * @brief 创建对象。
 * 
 * @param [in ] xpvt_reserved : 保留参数（可设置为 X_NULL）。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_writer_t::create(x_pvoid_t xpvt_reserved)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    do
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_CREATE_PDFLIB_NOT_LOADED, 0);
            break;
        }

        destroy();

        //======================================
        // 创建 PDF 文档控制句柄

        m_xht_fdocument = xfunc_CreateNewDocument();
        if (X_NULL == m_xht_fdocument)
        {
            xerr_no = XERR_MAKE(X_ERR_CREATE_XFUNC_CREATENEWDOC, 0);
            break;
        }

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (XERR_FAILED(xerr_no))
    {
        destroy();
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 销毁对象操作接口。
 */
x_void_t XPDF_writer_t::destroy(void)
{
    if (X_NULL != m_xht_fdocument)
    {
        xfunc_CloseDocument(m_xht_fdocument);
        m_xht_fdocument = X_NULL;
    }
}

/**********************************************************/
/**
 * @brief 获取页面数量。
 */
x_int32_t XPDF_writer_t::get_page_count(void) const
{
    if (!is_created())
    {
        return 0;
    }

    return xfunc_GetPageCount(m_xht_fdocument);
}

/**********************************************************/
/**
 * @brief 使用 RGB32 像素数据构建页面。
 * 
 * @param [in ] xit_index : 页面索引号。
 * @param [in ] xit_pcx   : 页面宽度。
 * @param [in ] xit_pcy   : 页面高度。
 * @param [in ] xbits_ptr : RGB32 (BGRA 顺序)的像素数据的缓存。
 * @param [in ] xit_pitch : RGB32 (BGRA 顺序)的像素数据的缓存行步进大小。
 * @param [in ] xit_icx   : 图像宽度。
 * @param [in ] xit_icy   : 图像高度。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_writer_t::insert_page_by_rgb32(
                                x_int32_t xit_index,
                                x_int32_t xit_pcx,
                                x_int32_t xit_pcy,
                                x_pvoid_t xbits_ptr,
                                x_int32_t xit_pitch,
                                x_int32_t xit_icx,
                                x_int32_t xit_icy)
{
    x_errno_t  xerr_no    = X_ERR_UNKNOW;
    x_handle_t xht_page   = X_NULL;
    x_handle_t xht_imgobj = X_NULL;
    x_handle_t xht_bitmap = X_NULL;

    do
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (!is_created())
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_THIS_UNCREATED, 0);
            break;
        }

        if ((xit_pcx <= 0) || (xit_pcy <= 0) ||
            (X_NULL == xbits_ptr) || (0 == xit_pitch) ||
            (xit_icx <= 0) || (xit_icy <= 0))
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_PARAM_INVALID, 0);
            break;
        }

        //======================================

        xht_bitmap = xfunc_Bitmap_CreateEx(
                            xit_icx,
                            xit_icy,
                            3,
                            xbits_ptr,
                            xit_pitch);
        if (X_NULL == xht_bitmap)
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_CREATE_BITMAP, 0);
            break;
        }

        xht_imgobj = xfunc_PageObj_NewImageObj(m_xht_fdocument);
        if (X_NULL == xht_imgobj)
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_NEW_IMGOBJ, 0);
            break;
        }

        xht_page = xfunc_Page_New(
                            m_xht_fdocument,
                            xit_index,
                            static_cast< x_lfloat_t >(xit_pcx),
                            static_cast< x_lfloat_t >(xit_pcy));
        if (X_NULL == xht_page)
        {
            xerr_no = XERR_MAKE(X_ERR_IPRGB_NEW_PAGE, 0);
            break;
        }

        //======================================

        xfunc_ImageObj_SetBitmap(&xht_page, 1, xht_imgobj, xht_bitmap);

        xfunc_PageObj_Transform(
                            xht_imgobj,
                            xit_pcx,
                            0.0,
                            0.0,
                            xit_pcy,
                            0.0,
                            0.0);

        xfunc_Page_InsertObject(xht_page, xht_imgobj);

        xfunc_Page_GenerateContent(xht_page);

        //======================================

        xfunc_PageObj_Destroy(xht_imgobj);
        xht_imgobj = X_NULL;

        xht_page = X_NULL;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xht_bitmap)
    {
        xfunc_Bitmap_Destroy(xht_bitmap);
        xht_bitmap = X_NULL;
    }

    if (X_NULL != xht_imgobj)
    {
        xfunc_PageObj_Destroy(xht_imgobj);
        xht_imgobj = X_NULL;
    }

    if (X_NULL != xht_page)
    {
        xfunc_ClosePage(xht_page);
        xht_page = X_NULL;

        if (is_created())
        {
            xfunc_Page_Delete(m_xht_fdocument, xit_index);
        }
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 使用 JPEG 图片文件构建页面。
 * 
 * @param [in ] xit_index : 页面索引号。
 * @param [in ] xszt_path : JPEG 图片文件路径名。
 * @param [in ] xit_pcx   : 页面宽度。
 * @param [in ] xit_pcy   : 页面高度。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_writer_t::insert_page_by_jpegfile(
                            x_int32_t   xit_index,
                            x_cstring_t xszt_path,
                            x_int32_t   xit_pcx,
                            x_int32_t   xit_pcy)
{
    x_errno_t  xerr_no       = X_ERR_UNKNOW;
    x_handle_t xht_page      = X_NULL;
    x_handle_t xht_imgobj    = X_NULL;

    x_handle_t xht_faccessor = X_NULL;

    do 
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (!is_created())
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_THIS_UNCREATED, 0);
            break;
        }

        if ((X_NULL == xszt_path) || (xit_pcx <= 0) || (xit_pcy <= 0))
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_PARAM_INVALID, 0);
            break;
        }

        //======================================

        xht_faccessor = file_open_accessor(xszt_path);
        if (X_NULL == xht_faccessor)
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_FILE_ACCESSOR, 0);
            break;
        }

        xht_imgobj = xfunc_PageObj_NewImageObj(m_xht_fdocument);
        if (X_NULL == xht_imgobj)
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_NEW_IMGOBJ, 0);
            break;
        }

        xht_page = xfunc_Page_New(
                            m_xht_fdocument,
                            xit_index,
                            static_cast< x_lfloat_t >(xit_pcx),
                            static_cast< x_lfloat_t >(xit_pcy));
        if (X_NULL == xht_page)
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_NEW_PAGE, 0);
            break;
        }

        //======================================

        if (!xfunc_ImageObj_LoadJpegFileInline(
                                &xht_page,
                                1,
                                xht_imgobj,
                                (XPDF_FILEACCESS *)xht_faccessor))
        {
            xerr_no = XERR_MAKE(X_ERR_IPJPG_LOAD_JPGFILE, 0);
            break;
        }

        xfunc_ImageObj_SetMatrix(xht_imgobj, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);

        xfunc_PageObj_Transform(
            xht_imgobj, xit_pcx, 0.0, 0.0, xit_pcy, 0.0, 0.0);

        xfunc_Page_InsertObject(xht_page, xht_imgobj);

        xfunc_Page_GenerateContent(xht_page);

        //======================================

        xfunc_PageObj_Destroy(xht_imgobj);
        xht_imgobj = X_NULL;

        xht_page = X_NULL;

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xht_faccessor)
    {
        file_close_accessor(xht_faccessor);
        xht_faccessor = X_NULL;
    }

    if (X_NULL != xht_imgobj)
    {
        xfunc_PageObj_Destroy(xht_imgobj);
        xht_imgobj = X_NULL;
    }

    if (X_NULL != xht_page)
    {
        xfunc_ClosePage(xht_page);
        xht_page = X_NULL;

        if (is_created())
        {
            xfunc_Page_Delete(m_xht_fdocument, xit_index);
        }
    }

    return xerr_no;
}

/**********************************************************/
/**
 * @brief 将当前构建的 PDF 内容保存至文件中。
 * 
 * @param [in ] xszt_path : 存储的文件路径。
 * 
 * @return x_errno_t : 错误码。
 * @retval X_ERR_OK, 操作成功。
 * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
 * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
 */
x_errno_t XPDF_writer_t::save_to_file(x_cstring_t xszt_path)
{
    x_errno_t xerr_no = X_ERR_UNKNOW;

    x_handle_t xht_fwriter = X_NULL;

    do 
    {
        //======================================

        if (X_NULL == xht_lib_instance)
        {
            xerr_no = XERR_MAKE(X_ERR_SAVE_PDFLIB_NOT_LOADED, 0);
            break;
        }

        if (!is_created())
        {
            xerr_no = XERR_MAKE(X_ERR_SAVE_THIS_UNCREATED, 0);
            break;
        }

        if (X_NULL == xszt_path)
        {
            xerr_no = XERR_MAKE(X_ERR_SAVE_PARAM_INVALID, 0);
            break;
        }

        //======================================

        xht_fwriter = file_open_writer(xszt_path);
        if (X_NULL == xht_fwriter)
        {
            xerr_no = XERR_MAKE(X_ERR_SAVE_FILE_WRITOR, last_error());
            break;
        }

        //======================================

        if (!xfunc_SaveAsCopy(m_xht_fdocument, (XPDF_FILEWRITE *)xht_fwriter, 2))
        {
            xerr_no = XERR_MAKE(X_ERR_SAVE_FILE_DOCUMENT, 0);
            break;
        }

        //======================================
        xerr_no = X_ERR_OK;
    } while (0);

    if (X_NULL != xht_fwriter)
    {
        file_close_writer(xht_fwriter);
        xht_fwriter = X_NULL;
    }

    return xerr_no;
}

////////////////////////////////////////////////////////////////////////////////
